package com.mrzhou.auth.config;

import com.mrzhou.auth.constant.AuthConstant;
import com.mrzhou.auth.filter.IntegrationAuthenticationClientFilter;
import com.mrzhou.auth.grant.IntegrationGranter;
import com.mrzhou.auth.service.CloudClientDetailsServiceImpl;
import com.mrzhou.auth.service.IntegrationUserDetailsImpl;
import com.mrzhou.auth.utils.TokenUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.servlet.Filter;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 认证服务配置
 */
@Order
@Configuration
@EnableAuthorizationServer
@Slf4j
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements ApplicationContextAware {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private IntegrationUserDetailsImpl integrationUserDetails;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private TokenEnhancer jwtTokenEnhancer;

    @Autowired
    private IntegrationExceptionTranslator integrationExceptionTranslator;

    @Autowired
    private IntegrationAuthenticationClientFilter integrationAuthenticationClientFilter;

    private ApplicationContext applicationContext;

    /**
     * 这里是配置是否需要使用标注的json返回格式
     */
    @Value("${spring.security.oauth2.use-default-oauth2-translator}")
    private boolean useDefaultOauth2Translator = true;

    /**
     * 配置认证管理器
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws  Exception{
        return configuration.getAuthenticationManager();
    }

    /**
     * 此处主要配置Token解析器和认证管理器
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore)
                // grant_type的扩展
                .tokenGranter(tokenGranter(endpoints))
                .authenticationManager(authenticationManager)
                .reuseRefreshTokens(false)
                .userDetailsService(integrationUserDetails);

        // 是否需要修改json的返回格式
        if(!useDefaultOauth2Translator) {
            log.info("不使用默认的认证异常的处理器");
            endpoints.exceptionTranslator(integrationExceptionTranslator);
        }

        // 扩展token的返回结果
        if(jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
            TokenEnhancerChain chain = new TokenEnhancerChain();
            List<TokenEnhancer> enhancerList = new ArrayList<>();
            enhancerList.add(jwtTokenEnhancer);
            enhancerList.add(jwtAccessTokenConverter);
            chain.setTokenEnhancers(enhancerList);
            //jwt
            endpoints.tokenEnhancer(chain)
                    .accessTokenConverter(jwtAccessTokenConverter);
        }
    }

    /**
     * 配置客户端信息
     */
    @Override
    @SneakyThrows
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        CloudClientDetailsServiceImpl clientDetailsService = new CloudClientDetailsServiceImpl(dataSource);
        clientDetailsService.setFindClientDetailsSql(AuthConstant.CLIENT_ORDER);
        clientDetailsService.setSelectClientDetailsSql(AuthConstant.SELECT_BY_CLIENT_ID);
        clients.withClientDetails(clientDetailsService);
    }

    /**
     *
     * @param oauthServer
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer){
        List<Filter> filterList = new ArrayList<>();
        if(!useDefaultOauth2Translator) {
            log.info("使用自定义的json格式");
            filterList.add(integrationAuthenticationClientFilter);
        }
//        filterList.add(new IntegrationAuthenticationFilter(applicationContext));
        oauthServer
            .allowFormAuthenticationForClients()
            .tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()")
            .tokenEndpointAuthenticationFilters(filterList);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 添加新的grant_type方式
     */
    private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
        // 获取目前grant_type支持的类型
        List<TokenGranter> granters = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
        IntegrationGranter.setGrantType(TokenUtil.AUTH_PSW);
        // 新的grant_type, 新增类型都在IntegrationGranter下处理登录逻辑
        // 二维码登录
        IntegrationGranter.setGrantType(TokenUtil.AUTH_QR);
        // 授权码登录(给第三方系统使用)
        IntegrationGranter.setGrantType(TokenUtil.AUTH_CODE);
        // 手机邮箱验证码登录
        IntegrationGranter.setGrantType(TokenUtil.AUTH_SMS);
        granters.addAll(IntegrationGranter.defaultIntegrationGranter(authenticationManager, endpoints));
        return new CompositeTokenGranter(granters);
    }


}
